package ace.cmp.spring.data.jdbc.querydsl.core.repository.handler.impl;

import ace.cmp.spring.data.jdbc.querydsl.core.model.PropertyRelation;
import ace.cmp.spring.data.jdbc.querydsl.core.repository.handler.QuerydslVersionAnnotationHandler;
import ace.cmp.spring.data.jdbc.querydsl.core.repository.handler.QuerydslVersionGenerator;
import ace.cmp.spring.data.jdbc.querydsl.core.util.QuerydslPropertyRelationUtils;
import com.querydsl.core.types.Expression;
import com.querydsl.core.types.Path;
import com.querydsl.sql.dml.SQLInsertClause;
import com.querydsl.sql.dml.SQLUpdateClause;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.data.annotation.Version;
import org.springframework.util.Assert;

/**
 * @author caspar
 * @date 2024/6/26 16:03 版本号注解处理器，用于底层乐观锁处理
 */
public class QuerydslVersionAnnotationHandlerImpl implements QuerydslVersionAnnotationHandler {

  private final QuerydslPropertyRelationUtils querydslPropertyRelationUtils;
  private final QuerydslVersionGenerator querydslVersionGenerator;

  public QuerydslVersionAnnotationHandlerImpl(
      QuerydslPropertyRelationUtils querydslPropertyRelationUtils,
      QuerydslVersionGenerator querydslVersionGenerator) {
    this.querydslPropertyRelationUtils = querydslPropertyRelationUtils;
    this.querydslVersionGenerator = querydslVersionGenerator;
  }

  @Override
  public void processInsert(Object entity, SQLInsertClause sqlClause, boolean enableVersionForce) {
    List<PropertyRelation> propertyRelationList = this.getPropertyRelationList(entity);
    if (CollectionUtils.isEmpty(propertyRelationList)) {
      return;
    }

    for (PropertyRelation propertyRelation : propertyRelationList) {
      Object userVersion = querydslPropertyRelationUtils.getValue(entity, propertyRelation);
      Object generateVersion = querydslVersionGenerator.generateNewValue(entity, propertyRelation);

      Assert.notNull(
          generateVersion,
          String.format(
              "optimistic lock value type is not support,name:%s,type:%s",
              propertyRelation.getField().getName(),
              propertyRelation.getField().getGenericType().getTypeName()
          )
      );

      Object value = userVersion;
      if (userVersion == null || enableVersionForce) {
        value = generateVersion;
      }

      querydslPropertyRelationUtils.setValue(entity, propertyRelation, value);
      sqlClause.set((Path) propertyRelation.getPath(), value);
    }
  }

  @Override
  public void processUpdate(
      Object entity, SQLUpdateClause sqlClause, boolean enableOnlyUpdateVersion) {
    this.getPropertyRelationList(entity)
        .forEach(
            propertyRelation -> {
              Object submitValue = querydslPropertyRelationUtils.getValue(entity, propertyRelation);
              if (submitValue == null && !enableOnlyUpdateVersion) {
                throw new RuntimeException("optimistic lock can not be null," + propertyRelation.getField().getName());
              }
              Expression<?> generateExpression = querydslVersionGenerator.generateNewExpression(entity, submitValue, propertyRelation);
              if (generateExpression == null) {
                throw new RuntimeException(
                    String.format(
                        "the type is not support,entity : %s, field name : %s",
                        entity.getClass().getTypeName(),
                        propertyRelation.getField().getName()
                    )
                );
              }
              sqlClause.set(propertyRelation.getPath(), (Expression) generateExpression);
              if (!enableOnlyUpdateVersion) {
                sqlClause.where(propertyRelation.getExpression().eq(submitValue));
              }
            });
  }

  private List<PropertyRelation> getPropertyRelationList(Object entity) {
    return this.querydslPropertyRelationUtils
        .getPropertyRelation(entity.getClass(), Version.class)
        .values()
        .stream()
        .collect(Collectors.toList());
  }
}
